package config

import (
	"encoding/json"
	"errors"
	"os"
)

// Manager centralises config loading and persistence for GUI usage.
type Manager struct {
	path    string
	cfg     *Config
	configs *Configurations
}

// NewManager returns a manager pointed at the default JSON config location.
func NewManager() *Manager {
	path, err := DefaultJSONPath()
	if err != nil {
		// Fall back to local file to keep the UI usable.
		path = defaultJSONName
	}

	return &Manager{
		path:    path,
		cfg:     &Config{},
		configs: &Configurations{},
	}
}

// Path exposes the underlying config file location.
func (m *Manager) Path() string {
	return m.path
}

// Load hydrates configuration values from env, JSON and legacy sources.
func (m *Manager) Load() error {
	// Try to load as new multi-config format first
	configs, err := LoadConfigurations(m.path)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return err
	}

	if configs != nil && len(configs.Configs) > 0 {
		m.configs = configs

		if m.configs.ActiveConfig == "" {
			m.configs.ActiveConfig = m.configs.Configs[0].Name
		}

		for i := range configs.Configs {
			cfg := &configs.Configs[i]
			if cfg.Name == m.configs.ActiveConfig {
				m.cfg = cfg
				if err := mergeLegacySources(m.cfg); err != nil {
					return err
				}
				applyEnvOverrides(m.cfg)
				if m.cfg.Provider == "" {
					m.cfg.Provider = ProviderCOS
				}
				return nil
			}
		}

		// 未找到匹配的激活配置，默认第一个
		m.cfg = &configs.Configs[0]
		if err := mergeLegacySources(m.cfg); err != nil {
			return err
		}
		applyEnvOverrides(m.cfg)
		if m.cfg.Provider == "" {
			m.cfg.Provider = ProviderCOS
		}
		return nil
	}

	// Fallback to single config mode
	cfg, err := LoadWithPath(m.path)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return err
	}

	if cfg == nil {
		cfg = &Config{}
	}

	m.cfg = cfg
	return nil
}

// Save writes the current configuration back to disk.
func (m *Manager) Save() error {
	if m.configs != nil && len(m.configs.Configs) > 0 {
		return SaveConfigsAsJSON(m.path, m.configs)
	}
	return SaveAsJSON(m.path, m.cfg)
}

// Config exposes the live configuration pointer.
func (m *Manager) Config() *Config {
	return m.cfg
}

// Configs returns all configurations
func (m *Manager) Configs() *Configurations {
	return m.configs
}

// SetConfig replaces the current configuration reference.
func (m *Manager) SetConfig(cfg *Config) {
	if cfg == nil {
		m.cfg = &Config{}
		return
	}

	if m.configs != nil && cfg.Name != "" {
		for i := range m.configs.Configs {
			if m.configs.Configs[i].Name == cfg.Name {
				m.configs.Configs[i] = *cfg
				m.cfg = &m.configs.Configs[i]
				return
			}
		}
	}

	m.cfg = cfg
}

// SetConfigs sets the full configuration set and selects one as the active configuration
func (m *Manager) SetConfigs(configs *Configurations) {
	if configs == nil {
		m.configs = &Configurations{}
		m.cfg = &Config{}
		return
	}
	m.configs = configs

	if len(configs.Configs) == 0 {
		m.cfg = &Config{}
		return
	}

	if configs.ActiveConfig == "" {
		configs.ActiveConfig = configs.Configs[0].Name
	}

	for i := range configs.Configs {
		if configs.Configs[i].Name == configs.ActiveConfig {
			m.cfg = &configs.Configs[i]
			return
		}
	}

	m.cfg = &configs.Configs[0]
	configs.ActiveConfig = configs.Configs[0].Name
}

// SetActiveConfig sets the active configuration
func (m *Manager) SetActiveConfig(name string) error {
	if m.configs == nil {
		return errors.New("no configurations loaded")
	}

	for i := range m.configs.Configs {
		if m.configs.Configs[i].Name == name {
			m.configs.ActiveConfig = name
			m.cfg = &m.configs.Configs[i]
			return nil
		}
	}

	return errors.New("configuration not found: " + name)
}

// LoadConfigurations loads multi-configuration file
func LoadConfigurations(path string) (*Configurations, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var configs Configurations
	if err := json.Unmarshal(data, &configs); err != nil {
		return nil, err
	}

	// Set default values
	for i := range configs.Configs {
		if configs.Configs[i].Provider == "" {
			configs.Configs[i].Provider = ProviderCOS
		}
	}

	return &configs, nil
}
